package main

import (
	"encoding/hex"
	"fmt"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"testCOM/mdc485"
	"testCOM/oc1519"
	"time"

	"github.com/tarm/serial"
)

/*
编译跨平台的只需要修改GOOS、GOARCH、CGO_ENABLED三个环境变量即可
GOOS：目标平台的操作系统(darwin、freebsd、linux、windows)
GOARCH：目标平台的体系架构32位还是64位(386、amd64、arm)

编译linux
set CGO_ENABLED=0
set GOARCH=amd64
set GOOS=linux
go build -ldflags "-w -s"

编译windows
set GOARCH=amd64
set GOOS=windows
go build -ldflags "-w -s"
*/

//COM口
var SerialComPort = ""

//COM口波特率
var SerialComBaud = "9600"

//设备地址
var DevicesAddr = 0x01

//发送的16进制字符串
var HexString = ""

//发送的16进制数据
var HexData []byte

//接收到的的16进制数据
var RecvHexData []byte

//协议类型
var ProtoType = ""

//协议指令码
var ProtoCmd = ""

const (
	PROTO_MDC485 = "mdc485"
	PROTO_OC1519 = "oc1519"
)

//后台运行
func Daemonize(args ...string) {
	sysType := runtime.GOOS
	if sysType == "linux" {
		var arg []string
		if len(args) > 1 {
			arg = args[1:]
		}
		fmt.Println("run in backMode")
		cmd := exec.Command(args[0], arg...)
		cmd.Env = os.Environ()
		cmd.Start()
	} else if sysType == "windows" {

	}
}

//16进制数组转16进制字符串
func HexToHexStr(buf []byte) string {
	len := len(buf)
	str := ""
	for i := 0; i < len; i++ {

		if i == 0 {
			str = fmt.Sprintf("%02X", buf[i])
		} else {
			str += fmt.Sprintf(" %02X", buf[i])
		}
	}
	return str
}

//往指定的COM口发送数据
func SendComData(comPort string, baud string, sendData []byte) {
	config := &serial.Config{
		Name:        comPort,
		ReadTimeout: time.Second * 1,
		Size:        8,
		Parity:      serial.ParityNone,
		StopBits:    serial.Stop1,
	}
	config.Baud, _ = strconv.Atoi(baud)
	nt, err := serial.OpenPort(config)
	if err != nil {
		fmt.Printf("Open Port [%s] fail,please check, %s", comPort, err.Error())
	}

	time.Sleep(time.Millisecond * 50)
	nt.Flush()
	fmt.Printf("SEND[%d]: %s\n", len(sendData), HexToHexStr(sendData))
	_, err = nt.Write(sendData)
	if err != nil {
		fmt.Printf("SEND err: %s\n", err)
	}
	var recvData []byte
	for {
		b := make([]byte, 1)
		n, err := nt.Read(b)
		if n == 0 {
			break
		}
		if err != nil { // 这里会有timeout
			fmt.Printf("RECV err: %s\n", err)
			break
		}
		recvData = append(recvData, b...)
	}
	RecvHexData = recvData
	fmt.Printf("RECV[%d]: %s\n", len(recvData), HexToHexStr(recvData))
	nt.Close()
}
func CmdHelp() {
	fmt.Print("serial test v1.0.0  by PPZ 2023/03/12\n" +
		"Usage: testCOM [-p PORT] [-d HexData] \n" +
		"\t-h 显示帮助信息\n" +
		"\t-p 串口号，默认串口 /dev/ttyUSB0\n" +
		"\t-b 串口波特率，默认9600\n" +
		"\t-d 发送的16进制数据，例如02014000004303\n" +
		"\t-a 10进制的设备地址，使用-d参数时不需要此参数\n\n" +
		"\t-c_mdc485 指令码(适用于MDCv1.0 485定制协议)，通常与-a参数同时使用，支持以下指令\n" +
		"\t\t status - 查询设备状态\n" +
		"\t\t setControl - 设置控制器参数: 互锁设置0、火警180、报警180、挟持密码85543889\n" +
		"\t-c_1519 指令码(适用于0c1519指纹锁协议)，支持以下指令\n" +
		"\t\t status - 查询设备状态\n" +
		"\n\t例如往串口/dev/ttyUSB0发送数据02014000004303\n" +
		"\t\ttestCOM -p /dev/ttyUSB0 -d 02014000004303\n" +
		"\n\t例如往串口/dev/ttyUSB0发送设置mdc485控制器参数，设备地址为6\n" +
		"\t\ttestCOM -p /dev/ttyUSB0 -a 6 -c_mdc485 status\n" +
		"\n\t例如往串口/dev/ttyUSB0发送查询指纹锁设备状态，设备地址为6\n" +
		"\t\ttestCOM -p /dev/ttyUSB0 -a 6 -c_1519 status\n\n" +
		"可在此处计算异或校验码http://www.metools.info/code/c48.html" +
		"\n")
}

//解析协议数据
func DecodeProto(RecvHexData []byte) {
	if len(RecvHexData) == 0 {
		return
	}
	switch ProtoType {
	case PROTO_MDC485:
		mdc485.DecodeProto(RecvHexData, ProtoCmd)
		break
	case PROTO_OC1519:
		oc1519.DecodeProto(RecvHexData, ProtoCmd)
		break
	}
}

//组包协议数据
func EncodeProto(add int, data *[]byte) {
	switch ProtoType {
	case PROTO_MDC485:
		mdc485.EncodeProto(ProtoCmd, add, data)
		break
	case PROTO_OC1519:
		oc1519.EncodeProto(ProtoCmd, add, data)
		break
	default:
		fmt.Printf("ProtoType:%s not support\n", ProtoType)
	}
}

func main() {
	//默认串口名称
	sysType := runtime.GOOS
	if sysType == "linux" {
		SerialComPort = "/dev/ttyUSB0"
	} else if sysType == "windows" {
		SerialComPort = "COM13"
	}
	args := os.Args
	if len(args) < 2 {
		CmdHelp()
		return
	}
	//输入参数解析
	for k, v := range args {
		switch v {
		case "-a":
			DevicesAddr, _ = strconv.Atoi(args[k+1])
			break
		case "-b":
			SerialComBaud = args[k+1]
			break
		case "-d":
			HexString = args[k+1]
			break
		case "-p":
			SerialComPort = args[k+1]
			break
		case "-c_mdc485":
			ProtoType = PROTO_MDC485
			ProtoCmd = args[k+1]
			break
		case "-c_1519":
			ProtoType = PROTO_OC1519
			ProtoCmd = args[k+1]
			break
		case "-h":
			CmdHelp()
			return
			break
		default:

		}
	}
	fmt.Printf("\nSerialComPort:[%s]\tDevicesAddr:[0x%02X] %d\n", SerialComPort, DevicesAddr, DevicesAddr)

	//ProtoType = "mdc485" //oc1519  mdc485
	//ProtoCmd = "status"
	if len(ProtoType) > 0 && len(ProtoCmd) > 0 {
		//组包协议数据
		EncodeProto(DevicesAddr, &HexData)
		fmt.Printf("%s[%s] send data:[%d]%s\n", ProtoType, ProtoCmd, len(HexData), HexToHexStr(HexData))
	} else { //用户指定发送数据
		HexData, err := hex.DecodeString(HexString)
		if err != nil {
			fmt.Printf("DecodeString[%s] error, please check\n", HexString)
			return
		}
		fmt.Printf("User send data:%s\n", HexToHexStr(HexData))
	}
	//往指定的COM口发送数据
	SendComData(SerialComPort, SerialComBaud, HexData)
	//解析协议数据
	DecodeProto(RecvHexData)
	fmt.Println("\nEND test\n")
}
